#include <physics/ValueLoader.h>
#include <physics/Node.h>
#include <physics/NodeLoader.h>
#include <physics/World.h>
#include <physics/Contact.h>

#include <Box2D/Dynamics/b2Body.h>
#include <Box2D/Dynamics/b2World.h>
#include <Box2D/Dynamics/b2Fixture.h>
#include <Box2D/Dynamics/Contacts/b2Contact.h>
#include <Box2D/Collision/Shapes/b2Shape.h>
#include <lib3d/node/Node.h>

namespace liba
{
namespace converter
{
	converter::EnumPair<char, b2BodyType> BodyTypeNames[] = 
	{
		converter::MakeEnumPair("StaticBody", b2_staticBody),
		converter::MakeEnumPair("KinematicBody", b2_kinematicBody),
		converter::MakeEnumPair("DynamicBody", b2_dynamicBody)
	};

	template<> struct ConverterTraits<char, b2BodyType>
		:	public ConverterTraitsEnumBase<char, b2BodyType, BodyTypeNames>
	{};

} // namespace converter
} // namespace liba

namespace physics
{

NodeLoader::NodeLoader(Node *node)
	: node(node)
	, massAttribute(0.0f)
{
	bodyDef.reset(new b2BodyDef());
}

NodeLoader::~NodeLoader()
{
}

bool NodeLoader::on_attribute(xml::Provider * prov, const std::string & name, const std::string & value)
{
	if(name == "Name")
	{
		node->name = Atom(value);
		return true;
	}
	if(name == "SceneNode")
	{
		node->sceneNodeName = Atom(value);
		return true;
	}
	if(name == "Attached")
		return converter::convert(value, &node->attachedToNode);

	return false;
}

bool NodeLoader::on_node(xml::Provider *prov, const std::string &name)
{
	if(name == "BodyType")
		return ValueReader::Read(prov, bodyDef->type);
	if(name == "Position")
		return ValueReader::Read(prov, bodyDef->position);
	if(name == "Rotation")
		return ValueReader::Read(prov, bodyDef->angle);
	if(name == "LinearVelocity")
		return ValueReader::Read(prov, bodyDef->linearVelocity);
	if(name == "AngularVelocity")
		return ValueReader::Read(prov, bodyDef->angularVelocity);
	if(name == "LinearDamping")
		return ValueReader::Read(prov, bodyDef->linearDamping);
	if(name == "AngularDamping")
		return ValueReader::Read(prov, bodyDef->angularDamping);
	if(name == "AllowSleep")
		return ValueReader::Read(prov, bodyDef->allowSleep);
	if(name == "Awake")
		return ValueReader::Read(prov, bodyDef->awake);
	if(name == "FixedRotation")
		return ValueReader::Read(prov, bodyDef->fixedRotation);
	if(name == "Bullet")
		return ValueReader::Read(prov, bodyDef->bullet);
	if(name == "Active")
		return ValueReader::Read(prov, bodyDef->active);
	if(name == "GravityScale")
		return ValueReader::Read(prov, bodyDef->gravityScale);
	if(name == "Mass")
		return ValueReader::Read(prov, massAttribute);
	if(name == "Fixture")
		return node->ParseFixtureDef(prov, &node->fixtureDef);

	return false;
}

std::auto_ptr <b2FixtureDef> NodeLoader::CreateFixtureDef()
{
	std::auto_ptr <b2FixtureDef> fixtureDef(new b2FixtureDef());
	fixtureDef->density = 1.0f;
	fixtureDef->friction = 0.0f;
	fixtureDef->restitution = 0.0f;

	return fixtureDef;
}

std::auto_ptr <b2Shape> NodeLoader::CreateShape()
{
	return std::auto_ptr <b2Shape> ();
}

bool NodeLoader::on_end_node(xml::Provider *prov)
{
	bodyDef->position.x       /= node->GetWorldPixelsInMeter();
	bodyDef->position.y       /= node->GetWorldPixelsInMeter();
	bodyDef->linearVelocity.x /= node->GetWorldPixelsInMeter();
	bodyDef->linearVelocity.y /= node->GetWorldPixelsInMeter();
	
	b2Body *body = node->GetB2World()->CreateBody(bodyDef.get());
	bodyDef.reset();

	std::auto_ptr <b2FixtureDef> newFixtureDef = node->fixtureDef;
	if(!newFixtureDef.get())
		newFixtureDef = CreateFixtureDef();

	std::auto_ptr <b2Shape> newShape = node->shape;
	if(!newShape.get())
		newShape = CreateShape();

	newFixtureDef->shape = newShape.get();

	body->CreateFixture(newFixtureDef.get());
	body->SetUserData(node);
	node->body = body;
	
	if(massAttribute > 0.0f)
		node->SetMass(massAttribute);

	return true;
}
	
}	// namespace physics